﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Data;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Text;

using zijian666.Converts;
using zijian666.Converts.Core;

namespace XUnitTest;

public class UnitTest1
{
    public UnitTest1()
    {
        if (!Debugger.IsAttached)
        {
            _ = DiagnosticListener.AllListeners.Subscribe(new DiagnosticObserver());
        }
    }
    private class User
    {
        public int ID { get; set; }
        public string Name { get; set; }
        public DateTime Birthday { get; set; }
        public bool Sex { get; set; }
    }

    private class MyClass
    {
        public int ID { get; set; }
        public string Name { get; set; }
    }

    [Fact]
    public void TimeTest1()
    {
        _ = "1".To<int>();
    }


    [Fact]
    public void TimeTest10000()
    {
        for (var i = 0; i < 10000; i++)
        {
            _ = "1".To<int>();
        }
    }

    [Fact]
    public void 一般转换测试()
    {
        Assert.Equal(16, "０Ｘ００１０".To<int>());
        Assert.Equal(1, "1".To<int>());
        Assert.Equal(true, "true".To<bool>());
        Assert.Equal(false, 0.To<bool>());
        Assert.Equal(true, 1.To<bool>());
        Assert.Equal(DateTime.Parse("2018-02-20 16:50:02"), "2018-02-20 16:50:02".To<DateTime>());
        Assert.Equal(DateTime.Parse("2018.02.20"), "2018.02.20".To<DateTime>());
        Assert.Equal(DateTime.Parse("16:50:09"), "16:50:09".To<DateTime>());
        Assert.Equal(TimeSpan.Parse("16:50:00"), "16:50".To<TimeSpan>());
        Assert.Equal(TimeSpan.Parse("16:50:09"), "16:50:09".To<TimeSpan>());
        Assert.Equal(TimeSpan.Parse("16:50:09.111"), "16:50:09.111".To<TimeSpan>());
        Assert.Equal(TimeSpan.Parse("16:50:09.111222"), "16:50:09.111222".To<TimeSpan>());
        Assert.Equal(1, "1".To<int>());
        Assert.Equal(null, "".To<int?>());
        Assert.Equal(1, "1".To<int?>());
        Assert.Equal(new Uri("http://a.com"), "a.com".To<Uri>());
        Assert.Equal(BitConverter.ToInt32(new byte[] { 255, 255, 0, 0 }, 0), new byte[] { 255, 255 }.To<int>());
        Assert.Equal(1519116602, "2018-02-20 16:50:02".To<int>());
        Assert.Equal(1519116602000, "2018-02-20 16:50:02".To<long>());
        Assert.Equal(DateTime.Parse("2018-02-20 16:50:02"), 1519116602.To<DateTime>());
        Assert.Equal(DateTime.Parse("2018-02-20 16:50:02"), 1519116602000L.To<DateTime>());
        Assert.Equal(DateTime.Parse("2018-02-20 16:50:02"), 1519116602L.To<DateTime>());
    }

    [Fact]
    public void 泛型转换测试()
    {
        var list = "1,2,3,4".To<List<int>>();
        Assert.Equal(list?.Count, 4);
        Assert.Equal(list[0], 1);
        Assert.Equal(list[1], 2);
        Assert.Equal(list[2], 3);
        Assert.Equal(list[3], 4);

        var dataset = new DataSet
        {
            Tables = {
                new DataTable("b")
                {
                    Columns =
                    {
                        {"id", typeof(object) },
                        {"birthday", typeof(string) }
                    },
                    Rows =
                    {
                        { 1, "2016-04-20" },
                        { "2", "2016-04-21" },
                        { new StringBuilder().Append('3'), "2016-04-22" },
                        { Guid.Empty, "2016-04-23" },
                    }
                }
            }
        };

        var dict = dataset.To<Dictionary<string, List<User>>>();

        Assert.Equal(dict?.Count, 1);
        Assert.Equal(dict["b"].Count, 4);
        Assert.Equal(dict["b"][0].ID, 1);
        Assert.Equal(dict["b"][1].ID, 2);
        Assert.Equal(dict["b"][2].ID, 3);
        Assert.Equal(dict["b"][3].ID, 0);
        Assert.Equal(dict["b"][0].Birthday, DateTime.Parse("2016-04-20"));
        Assert.Equal(dict["b"][1].Birthday, DateTime.Parse("2016-04-21"));
        Assert.Equal(dict["b"][2].Birthday, DateTime.Parse("2016-04-22"));
        Assert.Equal(dict["b"][3].Birthday, DateTime.Parse("2016-04-23"));
    }

    [Fact]
    public void 接口转换测试()
    {
        var list1 = "1,2,3,4".To<IList<int>>();
        Assert.Equal(list1?.Count, 4);
        Assert.Equal(list1[0], 1);
        Assert.Equal(list1[1], 2);
        Assert.Equal(list1[2], 3);
        Assert.Equal(list1[3], 4);

        var list11 = new object[] { "1", 2, 3, "4" }.To<IList<int>>();
        Assert.Equal(list11?.Count, 4);
        Assert.Equal(list11[0], 1);
        Assert.Equal(list11[1], 2);
        Assert.Equal(list11[2], 3);
        Assert.Equal(list11[3], 4);

        var list2 = "1,2,3,4".To<IList>();
        Assert.Equal(list2?.Count, 4);
        Assert.Equal(list2[0], "1");
        Assert.Equal(list2[1], "2");
        Assert.Equal(list2[2], "3");
        Assert.Equal(list2[3], "4");
    }

    [Fact]
    public void 自定义转换参数()
    {
        var result = "1;2;3;;4".Convert<List<int>>(new ConvertSettings()
        {
            StringSeparator = ";",
            StringSplitOptions = StringSplitOptions.None,
        });
        Assert.Equal(result.Success, false);
        var list = "1;2;3;;4".Convert<List<int>>(new ConvertSettings()
        {
            StringSeparator = ";",
            StringSplitOptions = StringSplitOptions.RemoveEmptyEntries,
        }).Value;
        Assert.Equal(list?.Count, 4);
        Assert.Equal(list[0], 1);
        Assert.Equal(list[1], 2);
        Assert.Equal(list[2], 3);
        Assert.Equal(list[3], 4);
    }

    [Fact]
    public void 自定义格式化参数()
    {
        var format = "yyyyMMddHHmmssffffff";
        var enUS = CultureInfo.CreateSpecificCulture("en-US");
        var urPK = CultureInfo.CreateSpecificCulture("ur-PK");
        var time = DateTime.Now;

        var formatResult = time.ToString(format);
        var enUSResult = time.ToString(enUS);
        var urPKResult = time.ToString(urPK);

        var formatTest = time.Convert<string>(new ConvertSettings().SetToStringArgument(typeof(DateTime), format)).Value;
        var enUSTest = time.Convert<string>(new ConvertSettings().SetToStringArgument(typeof(DateTime), enUS)).Value;
        var urPKTest = time.Convert<string>(new ConvertSettings().SetToStringArgument(typeof(DateTime), urPK)).Value;

        Assert.Equal(formatResult, formatTest);
        Assert.Equal(enUSResult, enUSTest);
        Assert.Equal(urPKResult, urPKTest);
    }

    [Fact]
    public void 测试友好类名()
    {
        Assert.Equal(typeof(int).To<string>(), "int");
        Assert.Equal(typeof(char?).To<string>(), "char?");
        Assert.Equal(typeof(List<ICollection>).To<string>(), "List<ICollection>");
        Assert.Equal(typeof((int, long)).To<string>(), "(int, long)");
        Assert.Equal(typeof(List<>).To<string>(), "List<>");
        Assert.Equal(typeof(void).To<string>(), "void");
        Assert.Equal(typeof(Dictionary<List<long?>, Tuple<string, DateTime, Guid, (TimeSpan, int)>>).To<string>(),
            "Dictionary<List<long?>, Tuple<string, DateTime, Guid, (TimeSpan, int)>>");
        Assert.Equal(typeof(int*).To<string>(), "int*");
        Assert.Equal(typeof(int).MakeByRefType().To<string>(), "int&");
        Assert.Equal(typeof(int[]).To<string>(), "int[]");
    }

    private enum MyEnum
    {
        A = 1,
        B = 2,
    }

    private class MyClass1
    {
        public MyEnum E { get; set; }
        public MyEnum? E2 { get; set; }
    }

    [Fact]
    public void 测试可空枚举()
    {
        var a = 0;
        var b = a.To<MyEnum?>();
        Assert.Equal((MyEnum?)0, b);
        object c = null;
        var d = c.To<MyEnum?>();
        Assert.Equal(null, d);

    }

    [Fact]
    public void 测试可空枚举属性()
    {
        var a = new { E2 = (MyEnum?)null };
        var b = a.To<MyClass1>();
        Assert.Equal(a.E2, b.E2);

        var c = new { E2 = 0 };
        var d = c.To<MyClass1>();
        Assert.Equal((MyEnum)0, d.E2);
    }

    [Fact]
    public void 测试枚举()
    {
        {
            var a = 0;
            var b = a.To<MyEnum>();
            Assert.Equal((MyEnum)0, b);
        }
        {
            var a = 1;
            var b = a.To<MyEnum>();
            Assert.Equal(MyEnum.A, b);
        }
        {
            var a = 3;
            var b = a.To<MyEnum>(0);
            Assert.Equal((MyEnum)0, b);
        }
        {
            var a = BindingFlags.Static | BindingFlags.Public;
            var b = a.ToString().To<BindingFlags>(0);
            Assert.Equal(a, b);
        }
    }

    [Fact]
    public void 测试枚举属性()
    {
        {
            var a = new { E = 0 };
            var b = a.To<MyClass1>();
            Assert.Equal((MyEnum)a.E, b.E);
        }
        {
            var a = new { E = 1 };
            var b = a.To<MyClass1>();
            Assert.Equal(MyEnum.A, b.E);
        }
        {
            var a = new { E = 3 };
            var b = a.To<MyClass1>(null);
            Assert.Equal(null, b);
        }
    }

    [Fact]
    public void 测试数组转型()
    {
        var arr = new[] { "1", "2", "3" };
        var arr2 = arr.To<int[]>();
        Assert.Equal(3, arr2?.Length);
        Assert.Equal(1, arr2[0]);
        Assert.Equal(2, arr2[1]);
        Assert.Equal(3, arr2[2]);
    }

    [Fact]
    public void 键值对接口转换测试()
    {
        var dict = new Dictionary<string, string[]>()
        {
            [" 2015-1-1"] = new[] { "1", "2", "3", "4" }
        };

        var dict2 = dict.To<IDictionary<DateTime, List<int>>>();
        Assert.NotNull(dict2);
        Assert.Equal(1, dict2.Count);
        Assert.Equal(new DateTime(2015, 1, 1), dict2.Keys.First());
        Assert.Equal(4, dict2.Values.First().Count);
        Assert.Equal(1, dict2.Values.First()[0]);
        Assert.Equal(2, dict2.Values.First()[1]);
        Assert.Equal(3, dict2.Values.First()[2]);
        Assert.Equal(4, dict2.Values.First()[3]);

        var nv = new NameValueCollection()
        {
            [" 2015-1-1"] = "1,2,3,4"
        };

        var dict3 = nv.To<IDictionary>();
        Assert.NotNull(dict3);
        Assert.Equal(1, dict3.Count);
        Assert.Equal(" 2015-1-1", dict3.Keys.Cast<object>().First());
        Assert.Equal("1,2,3,4", dict3.Values.Cast<object>().First());

    }

    [Fact]
    public void 进阶功能()
    {
        var uri = "www.baidu.com".To<Uri>();
        Assert.NotNull(uri);
        Assert.Equal("http://www.baidu.com/", uri.AbsoluteUri);

        var arr = "1,2,3,4,5,6".To<int[]>();
        Assert.NotNull(arr);
        Assert.Equal(6, arr.Length);
        Assert.Equal(1, arr[0]);
        Assert.Equal(2, arr[1]);
        Assert.Equal(3, arr[2]);
        Assert.Equal(4, arr[3]);
        Assert.Equal(5, arr[4]);
        Assert.Equal(6, arr[5]);

        var arr2 = "1,2,3,4,5,6".To<ArrayList>();
        Assert.NotNull(arr2);
        Assert.Equal(6, arr2.Count);
        Assert.Equal("1", arr2[0]);
        Assert.Equal("2", arr2[1]);
        Assert.Equal("3", arr2[2]);
        Assert.Equal("4", arr2[3]);
        Assert.Equal("5", arr2[4]);
        Assert.Equal("6", arr2[5]);

        var user = new { id = "1", name = 123 }.To<User>();
        Assert.NotNull(user);
        Assert.Equal(1, user.ID);
        Assert.Equal("123", user.Name);

        var dict = new Dictionary<string, string[]>()
        {
            [" 2015-1-1"] = new[] { "1", "2", "3", "4" }
        };
        var dict2 = dict.To<Dictionary<DateTime, List<int>>>();
        Assert.NotNull(dict2);
        Assert.Equal(1, dict2.Count);
        Assert.Equal(new DateTime(2015, 1, 1), dict2.Keys.First());
        Assert.Equal(4, dict2.Values.First().Count);
        Assert.Equal(1, dict2.Values.First()[0]);
        Assert.Equal(2, dict2.Values.First()[1]);
        Assert.Equal(3, dict2.Values.First()[2]);
        Assert.Equal(4, dict2.Values.First()[3]);

        Console.WriteLine("1,2,3,4,5".To<ArrayList>());
        var table = new DataTable("x");
        _ = table.Columns.Add("id", typeof(int));
        _ = table.Columns.Add("Name", typeof(string));
        _ = table.Columns.Add("Sex", typeof(bool));
        _ = table.Rows.Add(1, "blqw1", true);
        _ = table.Rows.Add(2, "blqw2", false);
        _ = table.Rows.Add(3, "blqw3", true);
        _ = table.Rows.Add(4, "blqw4", false);

        var list1 = table.To<List<NameValueCollection>>();
        Assert.NotNull(list1);
        Assert.Equal(4, list1.Count);

        void assert1(NameValueCollection actual, string id, string name, string sex)
        {
            Assert.NotNull(actual);
            Assert.Equal(id, actual["id"]);
            Assert.Equal(name, actual["Name"]);
            Assert.Equal(sex, actual["Sex"]);
        }
        assert1(list1[0], "1", "blqw1", "true");
        assert1(list1[1], "2", "blqw2", "false");
        assert1(list1[2], "3", "blqw3", "true");
        assert1(list1[3], "4", "blqw4", "false");

        var list2 = table.To<List<User>>();
        Assert.NotNull(list2);
        Assert.Equal(4, list2.Count);
        void assert2(User actual, int id, string name, bool sex)
        {
            Assert.NotNull(actual);
            Assert.Equal(id, actual.ID);
            Assert.Equal(name, actual.Name);
            Assert.Equal(sex, actual.Sex);
        }
        assert2(list2[0], 1, "blqw1", true);
        assert2(list2[1], 2, "blqw2", false);
        assert2(list2[2], 3, "blqw3", true);
        assert2(list2[3], 4, "blqw4", false);

        var list3 = table.To<List<Dictionary<string, object>>>();
        Assert.NotNull(list3);
        Assert.Equal(4, list3.Count);
        void assert3(Dictionary<string, object> actual, object id, object name, object sex)
        {
            Assert.NotNull(actual);
            Assert.Equal(id, actual["id"]);
            Assert.Equal(name, actual["Name"]);
            Assert.Equal(sex, actual["Sex"]);
        }
        assert3(list3[0], 1, "blqw1", true);
        assert3(list3[1], 2, "blqw2", false);
        assert3(list3[2], 3, "blqw3", true);
        assert3(list3[3], 4, "blqw4", false);

        var list4 = table.To<List<Hashtable>>();
        Assert.NotNull(list4);
        Assert.Equal(4, list4.Count);
        void assert4(Hashtable actual, object id, object name, object sex)
        {
            Assert.NotNull(actual);
            Assert.Equal(id, actual["id"]);
            Assert.Equal(name, actual["Name"]);
            Assert.Equal(sex, actual["Sex"]);
        }
        assert4(list4[0], 1, "blqw1", true);
        assert4(list4[1], 2, "blqw2", false);
        assert4(list4[2], 3, "blqw3", true);
        assert4(list4[3], 4, "blqw4", false);

        void assert5(DataRow actual, string id, string name, string sex)
        {
            Assert.NotNull(actual);
            Assert.Equal(id, actual["id"] + "");
            Assert.Equal(name, actual["Name"] + "");
            Assert.Equal(sex, (actual["Sex"] + "").ToLowerInvariant());
        }
        void assert6(DataTable actual)
        {
            Assert.NotNull(actual);
            Assert.Equal(4, actual.Rows.Count);
            assert5(actual.Rows[0], "1", "blqw1", "true");
            assert5(actual.Rows[1], "2", "blqw2", "false");
            assert5(actual.Rows[2], "3", "blqw3", "true");
            assert5(actual.Rows[3], "4", "blqw4", "false");
        }
        var tb1 = list1.To<DataTable>();
        assert6(tb1);
        var tb2 = list2.To<DataTable>();
        assert6(tb2);
        var tb3 = list3.To<DataTable>();
        assert6(tb3);
        var tb4 = list4.To<DataTable>();
        assert6(tb4);
    }

    [Fact]
    public void 空字符串转数组()
    {
        var str = "";
        var arr = str.To<string[]>();
        Assert.NotNull(arr);
        Assert.Equal(0, arr.Length);
    }

#pragma warning disable IDE1006 // 命名样式
    private class user
    {
        public long id { get; set; }
        public string name { get; set; }
        public DateTime date { get; set; }
    }
#pragma warning restore IDE1006 // 命名样式

    [Fact]
    public void 异常栈展示()
    {
        try
        {
            var table1 = new DataTable("a");
            _ = table1.Columns.Add("id", typeof(int));
            _ = table1.Columns.Add("name", typeof(string));
            _ = table1.Rows.Add(1, "赵");
            _ = table1.Rows.Add(2, "钱");
            _ = table1.Rows.Add(3, "孙");
            _ = table1.Rows.Add(4, "李");

            var table2 = new DataTable("b");
            _ = table2.Columns.Add("id", typeof(int));
            _ = table2.Columns.Add("date", typeof(string));
            _ = table2.Rows.Add(1, "2016-04-20");
            _ = table2.Rows.Add(2, "2016-04-21");
            _ = table2.Rows.Add(3, "2016-04-22A");
            _ = table2.Rows.Add(4, "2016-04-23");

            var dataset = new DataSet();
            dataset.Tables.Add(table1);
            dataset.Tables.Add(table2);

            _ = dataset.To<Dictionary<string, List<user>>>();
            Assert.Fail();
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.ToString());
        }
    }

    [Fact]
    public void 优化错误信息()
    {
        var test = 123456;
        var a = test.Convert(typeof(Convert));
        if (a.Success || !a.Exception.Message.Contains("静态类型"))
        {
            Assert.Fail();
        }

        var b = test.Convert(typeof(List<>));
        if (b.Success || !b.Exception.Message.Contains("泛型定义类型"))
        {
            Assert.Fail();
        }
    }

    [Fact]
    public void 优化单值转数组的体验()
    {
        var x = 123456;
        var a = x.To<string[]>();
        Assert.Single(a);
        Assert.Equal(x.ToString(), a[0]);
        var b = x.To<int[]>();
        Assert.Single(b);
        Assert.Equal(x, b[0]);
        var c = x.To<long[]>();
        Assert.Single(c);
        Assert.Equal(x, c[0]);
        var d = x.To<object[]>();
        Assert.Single(d);
        Assert.Equal(x, d[0]);

        var e = x.To<IList<string>>();
        Assert.Single(e);
        Assert.Equal(x.ToString(), e[0]);
        var f = x.To<IList<int>>();
        Assert.Single(f);
        Assert.Equal(x, f[0]);
        var g = x.To<IList<long>>();
        Assert.Single(g);
        Assert.Equal(x, g[0]);
        var h = x.To<IList<object>>();
        Assert.Single(h);
        Assert.Equal(x, h[0]);

        var i = x.To<IList>();
        Assert.Single(i);
        Assert.Equal(x, i[0]);

        var y = "2016-1-1";
        var j = y.To<DateTime[]>();
        Assert.Single(j);
        Assert.Equal(DateTime.Parse("2016-1-1"), j[0]);

    }

    [Fact]
    public void NullToObject()
    {
        object a = null;
        var b = a.To<object>();
        Assert.Equal(null, b);
    }

    [Fact]
    public void 数字大写转换()
    {
        var a = Converts.ToChineseNumber("123456456.789");
        Assert.Equal("一亿二千三百四十五万六千四百五十六点七八九", a);
        var b = Converts.ToChineseAmount("123456456.789", true);
        Assert.Equal("一亿二千三百四十五万六千四百五十六元七角八分", b);

        var c = Converts.ToChineseAmount("123456456.789");
        Assert.Equal("壹亿贰仟叁佰肆拾伍万陆仟肆佰伍拾陆元柒角捌分", c);
    }

    [Fact]
    public void 全半角转换()
    {
        var a = Converts.ToDBC("，１２３４５６７ａｋｓ");
        Assert.Equal(",1234567aks", a);
        var b = Converts.ToSBC("!1f23d.?@");
        Assert.Equal("！１ｆ２３ｄ．？＠", b);
        var c = Converts.ToSBC("恭喜发财!");
        Assert.Equal("恭喜发财！", c);
    }

    [Fact]
    public void 随机加密和校验()
    {
        var arr = new[]
        {
            Converts.ToRandomMD5("123456"),
            Converts.ToRandomMD5("123456"),
            Converts.ToRandomMD5("123456"),
            Converts.ToRandomMD5("123456"),
            Converts.ToRandomMD5("123456"),
        };

        foreach (var g in arr)
        {
            Console.WriteLine($"{g} : {Converts.EqualsRandomMD5("123456", g)}");
            Assert.True(Converts.EqualsRandomMD5("123456", g));
        }
    }

    [Fact]
    public void 匿名类转换()
    {
        var user1 = new { ID = 1, Name = "blqw" }.To<User>();
        Assert.Equal(1, user1.ID);
        Assert.Equal("blqw", user1.Name);

        var user2 = new { Id = 1, NAME = "blqw" }.To<User>();
        Assert.Equal(1, user2.ID);
        Assert.Equal("blqw", user2.Name);

        dynamic user3 = user1.Convert(new { ID = 1, Name = "blqw" }.GetType()).Value;
        Assert.Equal(1, user3.ID);
        Assert.Equal("blqw", user3.Name);

    }

    [Fact]
    public void 时间类型转换()
    {
        var a = DateTime.Parse("2020-10-06 14:48:01");
        var b = TimeSpan.Parse("14:48:01");
        var c = DateTimeOffset.Parse("2020-10-06 14:48:01 +08:00");

        Assert.Equal("2020-10-06 14:48:01", a.To<string>());
        Assert.Equal("14:48:01", b.To<string>());
        Assert.Equal("2020-10-06 14:48:01 +08:00", c.To<string>());

        Assert.Equal(b, a.To<TimeSpan>());
        Assert.Equal(c, a.To<DateTimeOffset>());
        Assert.Equal(b, c.To<TimeSpan>());
        Assert.Equal(a, c.To<DateTime>());

        Assert.Equal(a, "2020-10-06T14:48:01".To<DateTime>());
        Assert.Equal(a, "2020-10-06T06:48:01Z".To<DateTime>());

    }
}
